﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using TimerTask.Common.Utils;
using TimerTask.Dal;
using TimerTask.Model.Common;
using TimerTask.Model.Entity;

namespace TimerTask.Service
{
    public class QuartzService
    {
        private static bool _inited;

        private static IScheduler _scheduler;

        /// <summary>
        /// 初始化
        /// author:肖阳
        /// date:2016-10-27 16:39:24
        /// </summary>
        public static async Task Init()
        {
            LogProvider.SetCurrentLogProvider(new NLogProvider());
            if (_inited)
            {
                return;
            }
            _inited = true;

            LogUtil.Info("[0] 初始化定时任务开始..");

            StdSchedulerFactory factory = new StdSchedulerFactory();
            _scheduler = await factory.GetScheduler();
            await _scheduler.Start();

            //查询所有数据库上记录的所有任务
            List<JobInfo> jobs = new JobInfoDal().GetAllJobInfo();

            //获取正则运行的
            jobs = jobs.Where(x => x.JobStatus == JobStatus.Normal).ToList();

            //加载到程序中
            foreach (var jobInfo in jobs)
            {
                IJobDetail job = JobBuilder.Create<SysJob>()
                    .WithIdentity(jobInfo.Id.ToString(), jobInfo.GroupId.ToString())
                    .SetJobData(new JobDataMap((IDictionary<string, object>)jobInfo.ToDic()))
                    .Build();
                ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create()
                    .WithCronSchedule(jobInfo.Cron)
                    .Build();
                //加入作业调度池中
                await _scheduler.ScheduleJob(job, trigger);
                LogUtil.Info("[1] {0} 加入作业调度池中..", jobInfo.JobName, jobInfo.JobName);
            }
            LogUtil.Info("[1] 开启定时任务..");
        }

        /// <summary>
        /// 添加任务到调度
        /// author:肖阳
        /// date:2016-10-27 16:39:24
        /// </summary>
        /// <param name="jobInfo"></param>
        public static void AddJob(JobInfo jobInfo)
        {
            IJobDetail job = JobBuilder.Create<SysJob>()
                .WithIdentity(jobInfo.Id.ToString(), jobInfo.GroupId.ToString())
                .SetJobData(new JobDataMap((IDictionary<string, object>)jobInfo.ToDic()))
                .Build();
            ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create().WithCronSchedule(jobInfo.Cron).Build();
            _scheduler.ScheduleJob(job, new HashSet<ITrigger>() { trigger }, true);
            //    _scheduler.AddJob(job, true);
        }

        /// <summary>
        /// 触发一次
        /// author:肖阳
        /// date:2016-10-27 16:39:24
        /// </summary>
        /// <param name="jobInfo"></param>
        public static void Trigger(JobInfo jobInfo)
        {
            IJobDetail job = JobBuilder.Create<SysJob>()
                .SetJobData(new JobDataMap((IDictionary<string, object>)jobInfo.ToDic()))
                .Build();

            ITrigger trigger = TriggerBuilder.Create().StartNow().Build();
            _scheduler.ScheduleJob(job, new HashSet<ITrigger> { trigger }, true);
            _scheduler.AddJob(job, true);
        }

        /// <summary>
        /// 停止任务
        /// author:肖阳
        /// date:2016-10-27 16:39:24
        /// </summary>
        /// <param name="jobInfo"></param>
        public static void Stop(JobInfo jobInfo)
        {
            _scheduler.DeleteJob(new JobKey(jobInfo.Id.ToString(), jobInfo.GroupId.ToString()));
        }

        /// <summary>
        /// 获取下次执行时间
        /// author:肖阳
        /// date:2016-10-27 16:39:24
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static DateTime GetNextValidTime(string expression)
        {
            CronExpression ex = new CronExpression(expression);

            DateTimeOffset? validTime = ex.GetNextValidTimeAfter(DateTime.Now);

            if (validTime.HasValue)
            {
                return validTime.Value.LocalDateTime;
            }
            return DateTime.MaxValue;
        }

        private class NLogProvider : ILogProvider
        {
            public Logger GetLogger(string name)
            {
                return (level, func, exception, parameters) =>
                {
                    if (level >= LogLevel.Info && func != null)
                    {
                        LogUtil.Error("[" + DateTime.Now.ToString("T") + "] [" + level + "] " + func(), "QzLog", parameters);
                    }
                    return true;
                };
            }

            public IDisposable OpenNestedContext(string message)
            {
                throw new NotImplementedException();
            }

            public IDisposable OpenMappedContext(string key, string value)
            {
                throw new NotImplementedException();
            }
        }
    }


    /// <summary>
    /// 系统任务
    /// author:肖阳
    /// date:2016-10-28 14:17:43
    /// </summary>
    public class SysJob : IJob
    {
        /// <summary>
        /// 任务执行
        /// author:肖阳
        /// date:2016-10-27 16:39:24
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task Execute(IJobExecutionContext context)
        {
            int jobId = int.Parse(context.JobDetail.JobDataMap["Id"].ToString());
            string jobName = context.JobDetail.JobDataMap["JobName"].ToString();
            string requestUrl = context.JobDetail.JobDataMap["RequestUrl"].ToString();
            string resAssert = context.JobDetail.JobDataMap["ResAssert"].ToString();

            LogUtil.Info("[2] 触发\t" + jobName, jobName);
            //更新任务信息
            JobInfoDal dal = new JobInfoDal();
            dal.UpdateFireTime(jobId);

            //添加触发记录
            var getRes = HttpUtil.DoGet(requestUrl);

            TriggerLog log = new TriggerLog();
            log.JobId = jobId;
            log.TriggerTime = DateTime.Now;
            log.TriggerResult = getRes.Result;
            if (log.TriggerResult == resAssert)
            {
                //更新触发情况，结果通知
                log.TriggerStatus = TriggerStatus.Successed;
                AlarmService.Alarm(jobId, true, log.TriggerResult);
            }
            else
            {
                //报警
                log.TriggerStatus = TriggerStatus.Fail;
                LogUtil.Info("[3] 任务执行 {0} 失败：{1}", jobName, jobName, log.TriggerResult);
                AlarmService.Alarm(jobId, false, log.TriggerResult);
            }
            TriggerLogDal logDal = new TriggerLogDal();
            log.TriggerResult = log.TriggerResult.Length > 255 ? log.TriggerResult.Substring(0, 255) + "..." : log.TriggerResult;
            logDal.AddTriggerLog(log);
            return getRes;
        }
    }
}
